#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>

#include "../jng_load.h"

void* my_jng_alloc(size_t size)
{
	return malloc(size);
}

void my_jng_free(void* ptr)
{
	free(ptr);
}

void my_jng_errorproc(jng_handle handle, int errorCode, unsigned long chunkName, unsigned long chunkSeq, const char* erorText)
{
	printf("JNG error %d! chunk = %d, chunkSeq = %d, text = %s\r\n", errorCode, chunkName, chunkSeq, erorText);
}

int tga_save_pixels(const char *filename, short int width, short int height, unsigned char pixelDepth, void* imageData)
{
	unsigned char cGarbage = 0, type, mode;
	short int iGarbage = 0;
	int i, j;
	FILE *file;
	unsigned char* row;
	const unsigned char* imageDataRowPtr;
	int result = 1;

	/* open file and check for errors*/
	file = fopen(filename, "wb");
	if (file == NULL)
	{
		return 0;
	}

	/* compute image type: 2 for RGB(A), 3 for greyscale */
	mode = pixelDepth / 8;
	if ((pixelDepth == 24) || (pixelDepth == 32))
		type = 2;
	else if(pixelDepth == 8)
		type = 3;
	else
		return 0;

	/* write the header */
	fwrite(&cGarbage, sizeof(unsigned char), 1, file);
	fwrite(&cGarbage, sizeof(unsigned char), 1, file);

	fwrite(&type, sizeof(unsigned char), 1, file);

	fwrite(&iGarbage, sizeof(short int), 1, file);
	fwrite(&iGarbage, sizeof(short int), 1, file);
	fwrite(&cGarbage, sizeof(unsigned char), 1, file);
	fwrite(&iGarbage, sizeof(short int), 1, file);
	fwrite(&iGarbage, sizeof(short int), 1, file);

	fwrite(&width, sizeof(short int), 1, file);
	fwrite(&height, sizeof(short int), 1, file);
	fwrite(&pixelDepth, sizeof(unsigned char), 1, file);

	fwrite(&cGarbage, sizeof(unsigned char), 1, file);

	/* convert the image data from RGB(a) to BGR(A) */

	if (mode >=3)
	{
		/*RGB/RGBA*/
		row = (unsigned char*)malloc(width * mode);
		if(row==NULL)
		{
			fclose(file);
			return 0;
		}

		for (i=height - 1; i >= 0; i--)
		{
			imageDataRowPtr = ((const unsigned char*)imageData) + width * i * mode;

			for(j = 0; j < width * mode; j+=mode)
			{
				/*RGB -> BGR*/
				row[j + 2] = *imageDataRowPtr++;
				row[j + 1] = *imageDataRowPtr++;
				row[j + 0] = *imageDataRowPtr++;
				if(mode == 4)
				{
					/*Alpha*/
					row[j + 3] = *imageDataRowPtr++;
				}
			}
			fwrite(row, sizeof(unsigned char), width * mode, file);
		}
		free(row);
	}
	else if (mode == 1)
	{
		/*
		greyscale
		save the image data
		*/
		for (i=height - 1; i >= 0; i--)
		{
			imageDataRowPtr = ((const unsigned char*)imageData) + width * i;

			fwrite(imageDataRowPtr, sizeof(unsigned char), width, file);
		}
	}
	else
	{
		result = 0;
	}

	fclose(file);

	return result;
}


int main(int argc, char* argv[])
{
	jng_handle handle;
	FILE* file;
	unsigned char* imageLinePtr;
	unsigned char* imageBuffer;
	size_t	imageSize;
	size_t	imageWidth, imageHeight;
	size_t	rowPitch;
	int	colorBytesNum, alphaBytesNum, pixelBytesNum;
	int	colorComponentsCount;
	int	pixelBufferSize;
	unsigned char* pixelBuffer;
	size_t	currentLine;
	int readRes = 0;

	if(argc!=2)
	{
		printf("Error! Wrong args!\r\n");
		return -1;
	}

	file = fopen(argv[1], "rb");
	if(!file)
	{
		printf("Error! Can't open input file!\r\n");
		return -1;
	}
	fseek(file, 0, SEEK_END);
	imageSize = ftell(file);
	fseek(file, 0, SEEK_SET);

	imageBuffer = malloc(imageSize);
	if(fread(imageBuffer, 1, imageSize, file)!=imageSize)
	{
		fclose(file);				
		printf("Error! Can't read input file!\r\n");
		return -1;
	}
	fclose(file);
	file = NULL;

	handle = jng_create_from_data(NULL, imageBuffer, imageSize, my_jng_alloc, my_jng_free, my_jng_errorproc, JNG_FLAG_CRC_CRITICAL | JNG_FLAG_CRC_ANCILLARY | JNG_FLAG_STORE_ANCILLARY_CHUNKS);

	if(!handle)
	{
		free(imageBuffer);
		return -1;
	}

	if(!jng_read(handle))
	{
		jng_cleanup(handle);
		free(imageBuffer);
		return -1;
	}

	imageWidth = jng_get_image_width(handle);
	imageHeight = jng_get_image_height(handle);

	printf("image width %d, image height %d\r\n", imageWidth, imageHeight);

	jng_set_out_alpha_channel_bits(handle, JNG_OUT_BITS_8);
	jng_set_out_color_channel_bits(handle, JNG_OUT_BITS_8);
	jng_set_out_color_space(handle, JNG_RGB);
	jng_set_color_jpeg_src_type(handle, JNG_JPEG_SRC_DEFAULT);

	if(!jng_start_decode(handle))
	{
		jng_cleanup(handle);
		free(imageBuffer);
		return -1;
	}

	colorBytesNum = jng_get_out_color_channel_bytes(handle);
	alphaBytesNum = jng_get_out_alpha_channel_bytes(handle);
	colorComponentsCount = jng_get_out_color_components_num(handle);
	if(!colorBytesNum || !colorComponentsCount)
	{
		jng_finish_decode(handle);
		jng_cleanup(handle);
		free(imageBuffer);
		return -1;

	}
	pixelBytesNum = colorBytesNum * colorComponentsCount + alphaBytesNum;
	rowPitch = pixelBytesNum * imageWidth;
	pixelBufferSize = rowPitch * imageHeight;
	pixelBuffer = (unsigned char*)malloc(pixelBufferSize);

	if(!pixelBuffer)
	{
		jng_finish_decode(handle);
		jng_cleanup(handle);
		free(imageBuffer);
		return -1;
	}

	printf("Decoding image...\r\n");
	imageLinePtr = pixelBuffer;
	for(currentLine = 0; currentLine < imageHeight; currentLine++)
	{
		if(!jng_read_scanline(handle, imageLinePtr))
		{
			readRes = -1;
			break;
		}

		imageLinePtr += rowPitch;
	}

	jng_finish_decode(handle);
	jng_cleanup(handle);
	free(imageBuffer);
	
	/*Dump or show image buffer*/
	if(!readRes)
	{
		printf("Saving result...\r\n");
		readRes = tga_save_pixels("out.tga", imageWidth, imageHeight, pixelBytesNum * 8, pixelBuffer) == 0;
	}

	free(pixelBuffer);

	if(readRes)
	{
		printf("Successed.\r\n");
	}
	else
	{
		printf("Failed.\r\n");
	}

	return readRes;
}


